#include "CentralCache.hpp"
#include "PageCache.hpp"
CentralCache CentralCache::_sInc;
size_t CentralCache::FectRangObj(void *&start, void *&end, size_t batchNum, size_t size)
{
    size_t index = sizeclass::Index(size);
    _spanLists[index]._mutex.lock();

    Span *span = GetOneSpan(_spanLists[index], size);
    assert(span);
    assert(span->_freeList);

    size_t actalNum = 1;
    start = span->_freeList;
    end = start;
    while (batchNum-1 && (*(void **)end != nullptr))
    {
        end = *(void **)end;
        actalNum++;
        batchNum--;
    }
    span->_freeList = *(void **)end;
    *(void **)end = nullptr;
    span->_useCount += actalNum;
    _spanLists[index]._mutex.unlock();
    return actalNum;
}

Span *CentralCache::GetOneSpan(SpanList &list, size_t size)
{
    Span *it = list.Begain();
    while (it != list.End())
    {
        if (it->_freeList != nullptr)
        {
            return it;
        }
        else
        {
            it = it->_next;
        }
    }

    list._mutex.unlock();
    PageCache::GetInc()->_pageMtx.lock();
    Span *span = PageCache::GetInc()->NewSpan(sizeclass::NumMovePage(size));
    PageCache::GetInc()->_pageMtx.unlock();

    char *start = (char *)(span->_pageId << PAGE_SHIFT);
    size_t bytes = span->_n << PAGE_SHIFT;

    // 把大块内存切成size大小的对象链接起来
    char *end = start + bytes;
    // 先切一块下来去做尾，方便尾插
    span->_freeList = start;
    start += size;
    void *tail = span->_freeList;
    // 尾插
    while (start < end)
    {
        *(void **)(tail) = start;
        tail = *(void **)(tail);
        start += size;
    }
    *(void **)(tail) = nullptr; // 尾的指向置空

    list._mutex.lock();
    // 将切好的span头插到spanList
    _spanLists->PushFront(span);

    return span;
}

void CentralCache::ReleaseListToSpans(void *start, size_t size)
{
    size_t index = sizeclass::Index(size);
    _spanLists[index]._mutex.lock();

    while (start)
    {
        void *next = *(void **)start;
        Span *span = PageCache::GetInc()->MapObjectToSpan(start);
        *(void **)start = span->_freeList;
        span->_freeList = start;
        span->_useCount--;        // 更新被分配给thread cache的计数
        if (span->_useCount == 0) // 说明这个span分配出去的对象全部都回来了
        {
            // 此时这个span就可以再回收给page cache，page cache可以再尝试去做前后页的合并
            _spanLists[index].Erase(span);
            span->_freeList = nullptr; // 自由链表置空
            span->_next = nullptr;
            span->_prev = nullptr;

            // 释放span给page cache时，使用page cache的锁就可以了，这时把桶锁解掉
            _spanLists[index]._mutex.unlock();           // 解桶锁
            PageCache::GetInc()->_pageMtx.lock(); // 加大锁
            PageCache::GetInc()->ReleaseSpanToPageCache(span);
            PageCache::GetInc()->_pageMtx.unlock(); // 解大锁
            _spanLists[index]._mutex.lock();               // 加桶锁

            start = next;
        }

        _spanLists[index]._mutex.unlock();
    }
}